//
// Created by keep on 17-9-28.
//
#include "uart.h"
#include "zlog.h"
/**
*@brief   设置串口数据位，停止位和效验位
*@param  fd     类型  int  打开的串口文件句柄
*@param  speed 类型 speed_t 波特率
*@param  databits 类型  int 数据位   取值 为 7 或者8
*@param  stopbits 类型  int 停止位   取值为 1 或者2
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*/
int set_parity(int fd, speed_t speed, int databits, int stopbits, int parity) {
    struct termios options;
    int ret = -1;
    if ((ret = tcgetattr(fd, &options)) != 0)
    {
        dzlog_error("SetupSerial failed:%d", ret);
        return ret;
    }
    /*
    * Set the baud rates
    */

    ret = cfsetispeed(&options, speed);
    if(ret != 0){
        dzlog_error("cfsetispeed failed:%d", ret);
        return ret;
    }
    ret = cfsetospeed(&options, speed);
    if(ret != 0){
        dzlog_error("cfsetospeed failed:%d", ret);
        return ret;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits) /*设置数据位数*/
    {
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            dzlog_error("Unsupported data size");
            return -9;
    }
    switch (parity)
    {
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;   /* Clear parity enable */
            options.c_iflag &= ~INPCK;     /* Enable parity checking */
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;     /* Enable parity */
            options.c_cflag &= ~PARODD;   /* 转换为偶效验*/
            options.c_iflag |= INPCK;       /* Disnable parity checking */
            break;
        case 'S':
        case 's':  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            dzlog_error("Unsupported parity");
            return -9;
    }
    /* 设置停止位*/
    switch (stopbits)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            dzlog_error("Unsupported stop bits");
            return -9;
    }
    /* Set input parity option */
    if (parity != 'n')
    {
        options.c_iflag |= INPCK;
    }
    //经典输入是以面向行设计的
//    options.c_lflag |= (ICANON | ECHO | ECHOE);
    //使用原始模式(Raw Mode)方式来通讯,必须打开下边两个选项，并且linux串口设置只有重起后第一次有效，后续设置无效
    options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    options.c_oflag  &= ~OPOST;   /*Output*/
    options.c_iflag &= ~(ICRNL|IGNCR);
//    options.c_iflag &= ~(IXON | IXOFF | IXANY);//关闭流控
//    tcflush(fd, TCIFLUSH);	//溢出的数据可以接收，但不读
//    tcflush(fd, TCIOFLUSH);//clear io buff
    options.c_cc[VTIME] = 50; /* 设置超时5 seconds*///控制字符, 读取第一个字符的等待时间	unit: (1/10)second
    options.c_cc[VMIN] = 0; /* Update the options and do it NOW */	//控制字符, 所要读取字符的最小数量

    //设置新属性，TCSANOW：所有改变立即生效
    if ((ret=tcsetattr(fd, TCSANOW, &options)) != 0) {
        dzlog_error("the end SetupSerial failed :%d", ret);
        return ret;
    }
    dzlog_info("set_parity is ok");
    return 0;
}

/**
*@brief dxdd xxxxgglljj
*@param  fd     类型  int  打开的串口文件句柄
*@param  databits 类型  int 数据位   取值 为 7 或者8
*@param  stopbits 类型  int 停止位   取值为 1 或者2
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*/
int open_uart(const char *dev)
{
    int fd;
    /*以读写方式打开串口*/
//    fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);//O_NONBLOCK
    fd = open(dev, O_RDWR | O_NOCTTY);
    if (-1 == fd) {
        /* 不能打开串口*/
        dzlog_error("open %s failed:%d", dev, fd);
        return -1001;
    }

    int ret = -1;
    dzlog_info("open %s ok(%d)", dev, fd);
    ret = set_parity(fd, B115200, 8, 1, 'N');
    if(ret != 0){
        dzlog_error("set_parity failed:", ret);
        return -1002;
    }
    return fd;
}

int close_uart(int fd)
{
    close(fd);
}

int reopen_uart(const char *dev, int fd)
{
    if(dev != NULL && fd > 0){
        close_uart(fd);
        return open_uart(dev);
    }
}
size_t        /* Read "n" bytes from a descriptor */
readn(int fd, void *ptr, size_t n)
{
    size_t     nleft;
    ssize_t    nread;

    nleft = n;
    while (nleft > 0)
    {
        if ((nread = read(fd, ptr, nleft)) < 0)
        {
            if (nleft == n)
                return(-1);    /* error, return -1 */
            else
                break;        /* error, return amount read so far */
        }
        else if (nread == 0)
        {
            break;            /* EOF */
        }
        nleft -= nread;
        ptr   += nread;
    }
    return(n - nleft);             /* return >= 0 */
}

ssize_t        /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
    size_t    nleft;
    ssize_t   nwritten;

    nleft = n;
    while (nleft > 0)
    {
        if ((nwritten = write(fd, ptr, nleft)) < 0)
        {
            if (nleft == n)
                return(-1);    /* error, return -1 */
            else
                break;        /* error, return amount written so far */
        }
        else if (nwritten == 0)
        {
            break;
        }
        nleft -= nwritten;
        ptr   += nwritten;
    }
    return(n - nleft);    /* return >= 0 */
}
ssize_t tread(int fd, void *buf, size_t nbytes, unsigned int timout)
{

    int                         nfds;
    fd_set                   readfds;
    struct timeval  tv;
    tv.tv_sec = timout;
    tv.tv_usec = 0;
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);
    nfds = select(fd+1, &readfds, NULL, NULL, &tv);
    if (nfds <= 0) {
        if (nfds == 0)
            errno = ETIME;
        return(-1);
    }
    return(read(fd, buf, nbytes));
}

ssize_t treadn(int fd, void *buf, size_t nbytes, unsigned int timout)

{
    size_t      nleft;
    ssize_t     nread;
    nleft = nbytes;
    while (nleft > 0) {
        if ((nread = tread(fd, buf, nleft, timout)) < 0) {
            if (nleft == nbytes)
                return(-1); /* error, return -1 */
            else
                break;      /* error, return amount read so far */
        } else if (nread == 0) {
            break;          /* EOF */
        }
        nleft -= nread;
        buf += nread;
    }
    return(nbytes - nleft);      /* return >= 0 */
}